Перейти к основному содержимому

Деплой на VPS — Docker + Traefik

Версия: 1.0 Дата: 21.04.2026 Статус: Утверждён

Руководство описывает полный цикл развёртывания статического сайта документации на VPS с Docker и Traefik как обратным прокси. Этот вариант не требует Node.js или pm2 на сервере — доставляется только готовая сборка. Дополнительно описан альтернативный метод через pm2 + nginx.


Архитектура

Локальная машина VPS
───────────────── ───────────────────────────────────
docs/ ~/docs/
├── build/ ── rsync ──► ├── build/ (статика)
└── docker-compose ── rsync ──► └── docker-compose.yml

Docker: nginx:alpine
│ подключён к сети proxy

Traefik (уже работает)

HTTPS :443 → браузер

При деплое:

  1. Локально собирается статический HTML (npm run build)
  2. build/ и docker-compose.yml копируются на сервер через rsync по SSH
  3. На сервере поднимается контейнер nginx с примонтированным build/
  4. Traefik обнаруживает контейнер по labels и начинает маршрутизацию

Требования

На локальной машине

  • Node.js, npm — для сборки
  • rsync, ssh — для деплоя
  • SSH-ключ без пароля (BatchMode)

На сервере

  • Docker + Docker Compose
  • Traefik v2/v3 запущен в Docker с внешней сетью proxy
  • SSH-доступ по ключу

Подготовка — один раз

1. SSH-ключ для деплоя

Скрипты работают в BatchMode — без интерактивного ввода пароля. Нужен отдельный ключ.

# Сгенерировать ключ
ssh-keygen -t ed25519 -f ~/.ssh/vps-vlad -C "docs-deploy"

# В WSL нет GUI — используем sshpass для копирования ключа
sudo apt-get install -y sshpass
sshpass -p 'пароль' ssh-copy-id -o StrictHostKeyChecking=accept-new -i ~/.ssh/vps-vlad.pub -p 22 vlad@85.214.181.52

# Проверить BatchMode
ssh -o BatchMode=yes -i ~/.ssh/vps-vlad -p 22 vlad@85.214.181.52 "echo ok"
# Должно вывести: ok

Опционально — добавить в ~/.ssh/config:

Host vps-vlad
HostName 85.214.181.52
User vlad
Port 22
IdentityFile ~/.ssh/vps-vlad
IdentitiesOnly yes

2. Настроить cms-config.json

"vps": {
"domain": "85.214.181.52",
"ssh_host": "vlad@85.214.181.52",
"ssh_port": 22,
"ssh_key": "~/.ssh/vps-vlad",
"remote_path": "/home/vlad/docs",
"deploy_method": "docker",
"pm2_app": ""
}
ПолеОписание
domainДомен или IP — используется в выводе после деплоя
ssh_hostuser@host для SSH
ssh_portSSH порт (обычно 22)
ssh_keyПуть к приватному ключу (если пусто — использует агент)
remote_pathПапка на сервере куда деплоится проект
deploy_methoddocker или pm2 — метод деплоя
pm2_appИмя pm2-процесса (только для метода pm2)

3. Создать папку на сервере

ssh vlad@85.214.181.52 "mkdir -p ~/docs/build"

4. Проверить сеть Traefik на сервере

ssh vlad@85.214.181.52 "docker network ls | grep proxy"

Должна быть сеть с именем proxy. Если нет — уточни имя сети и поправь docker-compose.vps.yml.


Конфигурация контейнера

Файл docker-compose.vps.yml в корне репозитория:

services:
docs:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./build:/usr/share/nginx/html:ro
networks:
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.docs.rule=Host(`85.214.181.52`)
- traefik.http.routers.docs.entrypoints=websecure
- traefik.http.routers.docs.tls=true
- traefik.http.services.docs.loadbalancer.server.port=80

networks:
proxy:
external: true
ПараметрНазначение
nginx:alpineЛёгкий веб-сервер для раздачи статики
./build:/usr/share/nginx/html:roМонтируем сборку только для чтения
networks: proxyПодключаемся к сети Traefik
traefik.enable=trueРазрешаем Traefik управлять контейнером
Host(...)Правило маршрутизации по домену или IP
entrypoints=websecureСлушаем на порту 443 (HTTPS)
tls=trueTLS терминируется на Traefik

HTTPS по IP: При доступе по голому IP без домена Traefik выдаёт самоподписанный сертификат. Браузер покажет предупреждение — нажать «Продолжить» / «Перейти». После привязки домена предупреждение исчезнет (Let's Encrypt).


Деплой

Через панель управления (Tools UI)

Открыть http://localhost:3000/admin/tools.html → раздел «Публикация» → кнопка VPS · docker.

Из командной строки

tools/deploy_site.sh --mode=vps

Метод берётся из cms-config.json → vps.deploy_method. Переопределить явно:

tools/deploy_site.sh --mode=vps --method=docker
tools/deploy_site.sh --mode=vps --method=pm2

Что делает скрипт (метод docker)

  1. sync_assets — синхронизация медиафайлов из docs/*/assets/ в static/
  2. build_index — обновление главной страницы
  3. npm run build — сборка статического HTML в build/
  4. SSH check — проверка соединения с сервером
  5. rsync build/remote_path/build/ на сервере
  6. rsync docker-compose.vps.ymlremote_path/docker-compose.yml
  7. docker compose up -d — поднимает или перезапускает контейнер

Добавление домена или поддомена

Шаг 1. DNS-запись

В панели DNS вашего регистратора добавить A-запись:

docs.yourdomain.com A 85.214.181.52
# или поддомен:
api.yourdomain.com A 85.214.181.52

Изменения DNS распространяются от нескольких минут до 24 часов в зависимости от TTL.

Шаг 2. Обновить cms-config.json

"vps": {
"domain": "docs.yourdomain.com",
...
}

Шаг 3. Обновить docker-compose.vps.yml

Заменить правило маршрутизации и добавить certresolver:

labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.docs.rule=Host(`docs.yourdomain.com`)
- traefik.http.routers.docs.entrypoints=websecure
- traefik.http.routers.docs.tls=true
- traefik.http.routers.docs.tls.certresolver=letsencrypt
- traefik.http.services.docs.loadbalancer.server.port=80

Строка tls.certresolver=letsencrypt — Traefik автоматически получит сертификат Let's Encrypt. Имя резолвера должно совпадать с тем, что настроено в конфиге Traefik.

Шаг 4. Задеплоить

tools/deploy_site.sh --mode=vps --method=docker

Несколько сайтов на одном сервере

Каждый сайт — отдельный docker-compose.yml с уникальным именем роутера:

# Первый сайт
- traefik.http.routers.docs.rule=Host(`docs.yourdomain.com`)

# Второй сайт (в другом compose-файле)
- traefik.http.routers.api-docs.rule=Host(`api.yourdomain.com`)

Имена роутеров (docs, api-docs) должны быть уникальными в пределах Traefik.

Проверка сертификата

# Логи Traefik — должен появиться ACME-запрос
ssh vlad@85.214.181.52 "docker logs traefik 2>&1 | grep -i acme"

# Проверить сертификат
curl -I https://docs.yourdomain.com

Настройка Traefik на сервере

Этот раздел — для справки на случай если Traefik ещё не настроен.

docker-compose.yml для Traefik

services:
traefik:
image: traefik:v3
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# Раскомментировать для Let's Encrypt:
# - --certificatesResolvers.letsencrypt.acme.email=you@example.com
# - --certificatesResolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
# - --certificatesResolvers.letsencrypt.acme.tlsChallenge=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# - ./letsencrypt:/letsencrypt # нужно для хранения сертификатов
networks:
- proxy

networks:
proxy:
external: true

Создать внешнюю сеть один раз:

docker network create proxy

Деплой через pm2 + nginx (альтернатива)

Если на сервере нет Docker или предпочтителен pm2.

Требования на сервере

# Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# pm2 и serve
sudo npm install -g pm2 serve

cms-config.json для pm2

"vps": {
"deploy_method": "pm2",
"pm2_app": "docs",
"remote_path": "/home/vlad/docs",
...
}

Первоначальная настройка на сервере

ssh vlad@85.214.181.52

mkdir -p ~/docs/build
cd ~/docs

# Запустить serve как pm2-процесс
pm2 start serve --name docs -- build -p 3000
pm2 save
pm2 startup # скопировать и выполнить команду которую выведет pm2

Деплой

tools/deploy_site.sh --mode=vps --method=pm2

Скрипт выполняет rsync build/ → сервер, затем pm2 restart docs.

nginx как обратный прокси (для pm2)

sudo apt-get install -y nginx
sudo nano /etc/nginx/sites-available/docs
server {
listen 80;
server_name docs.yourdomain.com;

location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
sudo ln -s /etc/nginx/sites-available/docs /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# SSL через certbot
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d docs.yourdomain.com

Диагностика

Проверить что контейнер запущен

ssh vlad@85.214.181.52 "cd ~/docs && docker compose ps"

Посмотреть логи контейнера

ssh vlad@85.214.181.52 "cd ~/docs && docker compose logs --tail=50"

Проверить что Traefik видит контейнер

ssh vlad@85.214.181.52 "docker logs traefik 2>&1 | tail -30"

Принудительно пересоздать контейнер

ssh vlad@85.214.181.52 "cd ~/docs && docker compose down && docker compose up -d"

Только up -d не пересоздаёт контейнер если он уже существует с другими labels. down && up -d гарантирует применение изменённой конфигурации.


Сравнение методов

Docker + Traefikpm2 + nginx
Node.js на сервереНе нуженОбязателен
Обновление сайтаrsync + docker compose up -drsync + pm2 restart
SSLTraefik автоматическиcertbot вручную
Несколько сайтовLabels в compose-файлахОтдельные nginx-конфиги
Подходит еслиTraefik уже естьПростой сервер без Docker